<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://doc.hyperf.io
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
 */

namespace App\Controller;

use App\Manager\FrameManager;
use App\Manager\RoomManager;
use App\Manager\Storage;
use App\Manager\TaskManager;
use App\Manager\Util;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Swoole\Http\Request;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server;
use Hyperf\Utils\ApplicationContext;

class WebSocketController implements OnMessageInterface,OnOpenInterface, OnCloseInterface
{
    // 设置用户连接检验的延迟时间
    const DELAY_TIME = 5000;

    /**
     *
     * @var Storage
     */
//    protected $storage;

    /**
     * @Inject
     * @var RoomManager
     */
    protected $room;

    /**
     * @Inject
     * @var FrameManager
     */
    protected $frame;

    /**
     * @
     * @var TaskManager
     */
    protected $task;

    protected $server;

    // const PREFIX = "rzdt";

    const MESSAGE_MAX_LEN     = 1024; //单条消息不得超过1K

    const WORKER_HISTORY_ID   = 0;

    function __construct($config = array())
    {
        var_dump('begin construct');

        $config['storage'] = array(
            'history_num' => 100,
        );
        $config['whiteboard'] = array(
            'DOC_URL' => 'http://test.ruizhiedu.com:8080/pallasa_cloudteachzjc/whiteboard/api/pdf',
            'DIR_URL' => 'http://test.ruizhiedu.com:8080/pallasa_cloudteachzjc/upload/doc2pdf/',
            'DIR_PATH' => '/mnt/vdc1/nas/pallasa_upload/doc2pdf/'
        );
        /**
         * 使用文件或redis存储聊天信息
         */
//        $this->storage = Storage::instance($config['storage']);
        $storage = new Storage();
        $storage->flushKeys();
        $this->task = new TaskManager();
        $this->task->setConfig($config);
    }

    public function onOpen(Server $server, Request $request): void
    {
        // TODO: Implement onOpen() method.
        var_dump($request->fd.' enter');
        $this->server = $server;
        $storage = new Storage();
        $storage->pushUnuseList($request->fd);
        swoole_timer_tick(self::DELAY_TIME,
            function ($timmer, $param)
            {
                // $this->logDebug("验证是否是有效连接");
                $client_id = $param['client_id'];
                $server = $param['server'];
                $storage = $param['storage'];
                if ($storage->inUnuseLink($client_id))
                {
                    // $this->logDebug("关闭连接");
                    $storage->removeUnuseList($client_id);
                    $server->close($client_id);
                }
                swoole_timer_clear($timmer);
            },
            ['client_id'=>$request->fd, 'server' => $server, 'storage' => $storage]);
    }

    public function onMessage(Server $server, Frame $frame): void
    {
        // TODO: Implement onMessage() method.
        var_dump($frame);
        $ws = $frame->data;
        $client_id = $frame->fd;
        if (empty($ws)){
            return;
        }
        var_dump('begin message');
        var_dump($ws);
        // $this->log("onMessage #$client_id: " . $ws['message']);
        // 解析前端发来的数据
        // $this->logDebug($ws['message']);
        $msg = json_decode($ws, true);
        // $this->logDebug('接收数据');
        // $this->logDebug($msg);
        // $this->logDebug('client_id', $client_id);
        try
        {
            // 假设两端都登录成功的情况
            if (isset($msg['type']) && !empty($msg['type']))
            {
                if (method_exists($this, $msg['type']))
                {
                    $storage = new Storage();
                    $MsgSeq = empty($msg['MsgSeq']) ? 0 : $msg['MsgSeq'];
                    $storage->removeUnuseList($client_id);
                    call_user_func_array([$this, $msg['type']], [$client_id, $MsgSeq, $msg['data']]);
                    // $this->$msg['type']($client_id, $msg);
                }
                else
                {
                    throw new \Exception("非法访问", '1');
                }
            }
            else
            {
                // 验证消息格式
                if (Util::verifyInput($msg) === false)
                {
                    throw new \Exception('无效的数据格式！', '1');
                }
            }
        }
        catch(\Exception $e)
        {
            $err_msg = $e->getMessage();
            $code = $e->getCode();
            // 集中处理错误的请求
            $this->handleError($msg['MsgSeq'], $client_id, $err_msg, $code);
        }
    }

    /**
     * 得到任务 不是task任务
     * data 应该在进入前先验证
     */
    function onTask($data)
    {
        // 格式化任务
        if (is_string($data)) {
            $data = json_decode($data, true);
        } else {
            $data = json_decode($data['data'], true);
        }
        var_dump('before task', $data);
        // 获取任务状态
        try
        {
            // 阻塞任务
            $res = $this->task->addWork($data['data']['id']);
            // 完成
            $urls = $this->task->transToUrl($data['data']['id']);
            $return = [
                'client_id' => $data['client_id'], 'MsgSeq' => $data['MsgSeq'],
                'type' => 'init_page', 'data' => $urls
            ];
            var_dump('onTask', $return);
            return json_encode($return);
        }
        catch (\Exception $e)
        {
            $err_msg = $e->getMessage();
            $code = $e->getCode();
            // 集中处理错误的请求
            $this->handleError($data['MsgSeq'], $data['client_id'], $err_msg, $code);
            return json_encode([
                'client_id' => $data['client_id'], 'MsgSeq' => $data['MsgSeq'],
                'type' => 'error', 'data' => ''
            ]);
        }
    }

    public function onClose(\Swoole\Server $server, int $fd, int $reactorId): void
    {
        // TODO: Implement onClose() method.
    }

    function getpage($client_id, $MsgSeq, $data)
    {
        try
        {
            $task_data = ['client_id' => $client_id, 'MsgSeq' => $MsgSeq, 'data' => $data];
            if (empty($data['id']))
            {
                throw new \Exception("无效的请求！11", 1);
            }
            $this->onTask(json_encode($task_data));
        }
        catch (\Exception $e)
        {
            $err_msg = $e->getMessage();
            $code = $e->getCode();
            // 集中处理错误的请求
            $this->handleError($MsgSeq, $client_id, $err_msg, $code);
        }
    }

    /**
     * 进入房间所对应的某个页面
     */
    function in($client_id, $MsgSeq, $data)
    {
        // 验证进入房间所需的参数
        try
        {
            if (!Util::verifyEnterData($data))
                throw new \Exception("无效的数据结构！", 1);
            $id = $data['id'];
            $page = $data['page'];
            $key = $id . '-' . $page;
            $this->logDebug($key);
            if (!$this->room->enter($id, $page, $client_id))
                throw new \Exception("进入房间失败！", 1);
            // $this->storage->removeUnuseList($client_id);
            $frames = $this->frame->getData($key);
            $frames = empty($frames) ? [] : $frames;
            $this->sendJson($client_id,
                $this->createReturnMsg(0, $MsgSeq, '获取数据成功！', ['type' => 'init_frame', 'data' => $frames]));
        }
        catch (\Exception $e)
        {
            $err_msg = $e->getMessage();
            $code = $e->getCode();
            // 集中处理错误的请求
            $this->handleError($MsgSeq, $client_id, $err_msg, $code);
        }
    }

    /**
     * 退出
     */
    function out($client_id, $MsgSeq, $data=[])
    {
        try
        {
            $key = $this->room->map($client_id);
            if (!$this->room->leave($key, $client_id))
                throw new \Exception("离开房间失败！", 1);
            $this->sendJson($client_id,
                $this->createReturnMsg(0, $MsgSeq, '退出成功！'));
            $this->server->close($client_id);
        }
        catch (\Exception $e)
        {
            $err_msg = $e->getMessage();
            $code = $e->getCode();
            // 集中处理错误的请求
            $this->handleError($MsgSeq, $client_id, $err_msg, $code);
        }
    }

    /**
     * 画页面
     */
    function draw($client_id, $MsgSeq, $data)
    {
        try
        {
            $key = $this->room->map($client_id);
            $res = $this->frame->append($key, $data);
            $this->sendToOther($key, 'append_frame', $data, [$client_id], '收到帧！');
        }
        catch (\Exception $e)
        {
            // 暂时不处理
            $this->logDebug($e->getMessage());
        }
    }

    /**
     * 只有演讲者会调用这个方法
     */
    function changePage($client_id, $MsgSeq, $data)
    {

        try
        {
            $key = $this->room->map($client_id);
            $this->room->setNowPage($data['id'], $data['page']);
            // $this->sendToOther($key, 'change_page', $data, , );
            var_dump('changePage', $data);
            $members = $this->room->roomMembers($key, [$client_id]);
            $this->logDebug('room_members', $members);
            foreach ($members as $v)
            {
                $res = $this->sendJson($v,
                    $this->createReturnMsg(0, 0, '翻页',
                        ['type' => 'change_page', 'data' => $data['page']]));
                if (false === $res)
                {
                    // 用户或已经不存在，则删除
                    $this->room->leave($key, $v);
                }
            }
        }
        catch (\Exception $e)
        {
            // 暂时不处理
            $this->logDebug($e->getMessage());
        }
    }

    function roomPage($client_id, $MsgSeq, $data)
    {
        try
        {
            $key = $this->room->map($client_id);
            $page = $this->room->getNowPage($data['id']);
            $this->sendJson($client_id,
                $this->createReturnMsg(0, $MsgSeq, '获取数据成功！', ['type' => 'now_page', 'data' => $page]));
        }
        catch (\Exception $e)
        {
            // 暂时不处理
            $this->logDebug($e->getMessage());
        }
    }

    /**
     * 撤销
     * 方案: 客户端传来要删除的关键帧数组，服务端遍历从尾到头开始删 lrem(key, count, value);
     */
    function undo($client_id, $MsgSeq, $data)
    {
        try
        {
            $key = $this->room->map($client_id);
            $data = empty($data['data']) ? [] : $data['data'];
            $frames = $this->frame->undo($key, $data);
            $this->sendToOther($key, 'init_frame', $frames, [], '撤销帧');
        }
        catch (\Exception $e)
        {
            // 暂时不处理
            $this->logDebug($e->getMessage());
        }
    }

    function clear($client_id, $MsgSeq, $data)
    {
        try
        {
            $key = $this->room->map($client_id);
            $frames = $this->frame->clean($key);
        }
        catch (\Exception $e)
        {
            // 暂时不处理
            $this->logDebug($e->getMessage());
        }
    }

    function handleError($MsgSeq, $client_id, $err_msg, $code)
    {
        $flag = $code == 0 ? 'OK' : 'ERROR';
        $this->sendJson($client_id,
            $this->createReturnMsg($code, $MsgSeq, $err_msg));
    }

    /**
     * 给其他人发送广播， 不对数据做任何处理
     */
    function broadcastToOther($client_id, $MsgSeq, $data)
    {
        try
        {
            $key = $this->room->map($client_id);
            $this->sendToOther($key, 'broadcastToOther', $data, [$client_id], $msg='收到广播！');
        }
        catch (\Exception $e)
        {
            // 暂时不处理
            $this->logDebug($e->getMessage());
        }
    }

    /**
     * 给所有人发送信息，包括自己
     */
    function broadcastToAll($client_id, $MsgSeq, $data)
    {
        try
        {
            $this->logDebug($client_id);
            $key = $this->room->map($client_id);
            $this->logDebug($key);
            $this->sendToOther($key, 'broadcastToAll', $data, [], $msg='收到广播！');
        }
        catch (\Exception $e)
        {
            // 暂时不处理
            $this->logDebug($e->getMessage());
        }
    }

    function sendToOther($key, $type, $data, $except=[], $msg='发送消息！')
    {
        $members = $this->room->members($key, $except);
        foreach ($members as $v)
        {
            $res = $this->sendJson($v,
                $this->createReturnMsg(0, 0, $msg,
                    ['type' => $type, 'data' => $data]));
            if (false === $res)
            {
                // 用户或已经不存在，则删除
                $this->room->leave($key, $v);
            }
        }
    }



    /**
     * 发送JSON数据
     * @param $client_id
     * @param $array
     */
    function sendJson($client_id, $array)
    {
        $msg = json_encode($array);
        // $this->logDebug('发送数据！');
        // $this->logDebug($array);
        return $this->server->push($client_id, $msg);
    }

    function createReturnMsg($state, $MsgSeq, $msg, $data=[])
    {
        return [
            'state' => $state,
            'msg' => $msg,
            'MsgSeq' => $MsgSeq,
            'data' => $data
        ];
    }


    /**
     * 统一debug显示管理，取消使用...$str的语法,兼容低版本php
     */
    function logDebug()
    {
        $args = func_get_args();
        call_user_func_array('var_dump', $args);
    }
}
